#ifndef ARRAYCONTAINER_H__
#define ARRAYCONTAINER_H__

///////////////////////////////////////////////////////////////////////////////
//
//	class:			ArrayContainer
//
//	description:	This template implements an array container class for any
//					type of variable. Its important that whatever type is used
//					with this class have copy methods implemented (i.e. copy
//					constructor and equals operator).
//
//	purpose:		This class manages an array of objects of type T. It uses
//					a variable sized buffer to allocate and de-allocate data.
//
///////////////////////////////////////////////////////////////////////////////

template <class T> class ArrayContainer {
	void InitArray(void)
	{
		m_data = 0;
		m_size = m_total_size = 0;
		m_buffer_size = 16;
	}
protected:
	T * m_data;
	int m_size, m_total_size, m_buffer_size;
protected:
	T * AllocT(int size)
	{
		return (new T[size]);
	}
	void FreeT(T * ptr)
	{
		if (ptr)
		{
			delete [] ptr;
		}
	}
public:
	ArrayContainer(void){ InitArray();}
	ArrayContainer(const ArrayContainer<T>& cont)
	{
		InitArray();
		Copy(cont);
	}
	void CreateArray(int n)
	{
		ClearArray();
		m_size = m_total_size = n;
		m_data = AllocT(n);
	}
	const ArrayContainer<T>& GetArrayContainer(void)const{ return *this;}
	void Copy(const ArrayContainer<T>& cont)
	{
		int size = cont.GetSize();

		if (m_total_size < size)
		{
			ClearArray();
			m_data = AllocT(size);
			m_total_size = size;
		}
		m_size = size;

		T data;
		for (int i=0;i<size;i++){
			if (cont.GetElement(i,data))
			{
				m_data[i] = data;
			}
		}

		m_buffer_size = cont.GetBufferSize();
	}
	ArrayContainer& operator = (const ArrayContainer<T>& cont)
	{
		Copy(cont);
		return *this;
	}
	~ArrayContainer(void){ ClearArray();}
	T * GetData(void)
	{
		return m_data;
	}
	const T * GetConstData(void)const
	{
		return m_data;
	}
	T& operator [] (int index)
	{
		return m_data[index];
	}
	void ClearArray(void)
	{
		FreeT(m_data);
		int buf_size = m_buffer_size;
		InitArray();
		SetBufferSize(buf_size);
	}
	void ResetArray(void)
	{
		m_size = 0;
	}
	int  SetSize(int size)
	{
		if (size>0)
		{
			if (!m_data)
			{
				m_total_size = size;
				m_data = AllocT(m_total_size);
				m_size = 0;
			}
			else if (size > m_total_size)
			{
				T * newdata = AllocT(size);
				for (int i=0;i<m_size;i++){
					newdata[i] = m_data[i];
				}
				FreeT(m_data);
				m_data = newdata;
				m_total_size = size;
			}
		}

		return m_total_size;
	}
	int  GetSize(void)const
	{
		return m_size;
	}
	int  GetTotalSize(void)const
	{
		return m_total_size;
	}
	int  AddElement(const T& data)
	{
		int result = m_size;

		if (!m_data)
		{
			m_total_size = m_buffer_size;
			m_data = AllocT(m_total_size);
			m_data[0] = data;
			m_size = 1;
		}
		else if (m_size == m_total_size)
		{
			m_total_size += m_buffer_size;
			T * newdata = AllocT(m_total_size);
			for (int i=0;i<m_size;i++){
				newdata[i] = m_data[i];
			}
			FreeT(m_data);
			m_data = newdata;
			m_data[m_size] = data;
			m_size++;
		}
		else if (m_size < m_total_size)
		{
			m_data[m_size] = data;
			m_size++;
		}

		return (m_size - result);
	}
	int InsertElement(int index, T& data)
	{
		int result = 0;

		if (index >= 0 && index < m_size)
		{
			if (m_size == m_total_size)
			{
				m_total_size += m_buffer_size;
				T * newdata = AllocT(m_total_size);
				for (int i=0;i<=m_size;i++){
					if (i == index)
					{
						newdata[i] = data;
						i++;
					}
					newdata[i] = data;
				}
				FreeT(m_data);
				m_data = newdata;
				m_size++;
				result++;
			}
			else if (m_size < m_total_size)
			{
				for (int i=m_size;i>index;i--){
					m_data[i] = m_data[i-1];
				}
				m_data[index] = data;
				m_size++;
				result++;
			}
		}
		else if (index == m_size)
		{
			result = AddElement(data);
		}

		return result;
	}
	int  DeleteElement(int index)
	{
		int result = 0;

		if (index>= 0 && index<m_size)
		{
			m_size--;
			for (int i=index;i<m_size;i++){
				m_data[i] = m_data[i+1];
			}
			result++;
		}

		return result;
	}
	int  GetElement(int index, T& data)const
	{
		int result = 0;

		if (index>=0 && index<m_size)
		{
			data = m_data[index];
			result++;
		}

		return result;
	}
	T Element(int index)
	{
		return m_data[index];
	}
	int  ChangeElement(int index, T& data)
	{
		int result = 0;

		if (index>=0 && index<m_size)
		{
			m_data[index] = data;
			result++;
		}

		return result;
	}
	int  GetBufferSize(void)const
	{
		return m_buffer_size;
	}
	int  SetBufferSize(int size)
	{
		int result = 0;

		if (size>0)
		{
			m_buffer_size = size;
			result++;
		}

		return result;
	}
};

#endif
